/*
 * File      : spi_flash_w25qxx.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006 - 2011, RT-Thread Development Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Change Logs:
 * Date           Author       Notes
 * 2011-12-16     aozima       the first version
 * 2012-05-06     aozima       can page write.
 * 2012-08-23     aozima       add flash lock.
 * 2012-08-24     aozima       fixed write status register BUG.
 */

#include <stdint.h>
#include "rtthread.h"
#include "main.h"
//#include "stm32f4xx_hal.h"
#include "ipc/completion.h"
#include "spi_flash_w25qxx.h"
static rt_mutex_t spi_flash_mutex;
#define F_CS_Pin 				W25_CS_Pin 
#define F_CS_GPIO_Port 	W25_CS_GPIO_Port 
//////////////////////////////////////////////////////////////////////////////////
//本程序只供学习使用，未经作者许可，不得用于其它任何用途
//ALIENTEK STM32F407开发板
//W25QXX驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2017/4/13
//版本：V1.0
//版权所有，盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//////////////////////////////////////////////////////////////////////////////////

u16 W25QXX_TYPE=W25Q128;	//默认是W25Q256

//4Kbytes为一个Sector
//16个扇区为1个Block
//W25Q256
//容量为32M字节,共有512个Block,8192个Sector
extern SPI_HandleTypeDef hspi2;
#define SpiHandle	&hspi2
static struct rt_completion spi_com;

//初始化SPI FLASH的IO口
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
    rt_completion_done(&spi_com);
}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
    rt_completion_done(&spi_com);
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
    rt_completion_done(&spi_com);
}
void W25QXX_Init(void)
{
    u8 temp;
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_SET);			                //SPI FLASH不选中
    W25QXX_TYPE=W25QXX_ReadID();	        //读取FLASH ID.
    if(W25QXX_TYPE==W25Q256)                //SPI FLASH为W25Q256
    {
        temp=W25QXX_ReadSR(3);              //读取状态寄存器3，判断地址模式
        if((temp&0X01)==0)			        //如果不是4字节地址模式,则进入4字节地址模式
        {
            temp=W25X_Enable4ByteAddr;
            rt_completion_init(&spi_com);
            HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_RESET); 			        //选中
            HAL_SPI_Transmit_IT(SpiHandle,&temp,1);//发送进入4字节地址模式指令
            rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
            HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_SET);       		        //取消片选

        }
    }
}

//读取W25QXX的状态寄存器，W25QXX一共有3个状态寄存器
//状态寄存器1：
//BIT7  6   5   4   3   2   1   0
//SPR   RV  TB BP2 BP1 BP0 WEL BUSY
//SPR:默认0,状态寄存器保护位,配合WP使用
//TB,BP2,BP1,BP0:FLASH区域写保护设置
//WEL:写使能锁定
//BUSY:忙标记位(1,忙;0,空闲)
//默认:0x00
//状态寄存器2：
//BIT7  6   5   4   3   2   1   0
//SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
//状态寄存器3：
//BIT7      6    5    4   3   2   1   0
//HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
//regno:状态寄存器号，范:1~3
//返回值:状态寄存器值
u8 W25QXX_ReadSR(u8 regno)
{
    u8 byte=0,command=0;
    switch(regno)
    {
        case 1:
            command=W25X_ReadStatusReg1;    //读状态寄存器1指令
            break;
        case 2:
            command=W25X_ReadStatusReg2;    //读状态寄存器2指令
            break;
        case 3:
            command=W25X_ReadStatusReg3;    //读状态寄存器3指令
            break;
        default:
            command=W25X_ReadStatusReg1;
            break;
    }
    rt_completion_init(&spi_com);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_RESET); 			        //选中
    HAL_SPI_Transmit_IT(SpiHandle,&command,1);
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    rt_completion_init(&spi_com);
    HAL_SPI_Receive_IT(SpiHandle,&byte,1);
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_SET);       		        //取消片选


    return byte;
}
//写W25QXX状态寄存器
void W25QXX_Write_SR(u8 regno,u8 sr)
{
    u8 command=0;
    switch(regno)
    {
        case 1:
            command=W25X_WriteStatusReg1;    //写状态寄存器1指令
            break;
        case 2:
            command=W25X_WriteStatusReg2;    //写状态寄存器2指令
            break;
        case 3:
            command=W25X_WriteStatusReg3;    //写状态寄存器3指令
            break;
        default:
            command=W25X_WriteStatusReg1;
            break;
    }
    u8 temp[2];
    temp[0]=command;
    temp[1]=sr;

    rt_completion_init(&spi_com);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_RESET); 			        //选中
    HAL_SPI_Transmit_IT(SpiHandle,temp,2);
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_SET); 			        //选中
}
//W25QXX写使能
//将WEL置位
void W25QXX_Write_Enable(void)
{
    u8 temp=W25X_WriteEnable;
    rt_completion_init(&spi_com);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_RESET); 			        //选中
    HAL_SPI_Transmit_IT(SpiHandle,&temp,1);
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_SET); 			        //选中
}
//W25QXX写禁止
//将WEL清零
void W25QXX_Write_Disable(void)
{
    u8 temp=W25X_WriteDisable;
    rt_completion_init(&spi_com);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_RESET); 			        //选中
    HAL_SPI_Transmit_IT(SpiHandle,&temp,1);
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_SET); 			        //选中
}

//读取芯片ID
//返回值如下:
//0XEF13,表示芯片型号为W25Q80
//0XEF14,表示芯片型号为W25Q16
//0XEF15,表示芯片型号为W25Q32
//0XEF16,表示芯片型号为W25Q64
//0XEF17,表示芯片型号为W25Q128
//0XEF18,表示芯片型号为W25Q256
u16 W25QXX_ReadID(void)
{
    u8 temp[4] = {0x90,0,0,0};
    rt_completion_init(&spi_com);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_RESET); 			        //选中
    HAL_SPI_Transmit_IT(SpiHandle,temp,4);
		rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
		rt_completion_init(&spi_com);
    HAL_SPI_Receive_IT(SpiHandle,temp,2);
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_SET);
    return temp[0]<<8|temp[1];
}
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大4096)
void W25QXX_Read_Sector(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
    //u16 i;
    u8 temp[4]={W25X_ReadData,(ReadAddr)>>16,(ReadAddr)>>8,(ReadAddr)};
    rt_completion_init(&spi_com);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_RESET);                            //使能器件
    HAL_SPI_Transmit_IT(SpiHandle,temp,4);
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    rt_completion_init(&spi_com);
    //HAL_SPI_Receive_IT(SpiHandle,pBuffer,NumByteToRead);
		HAL_SPI_Receive_DMA(SpiHandle,pBuffer,NumByteToRead);
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_SET);
}
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 size)
{
    //u16 i;
    u8 temp[4]={W25X_PageProgram,(u8)((WriteAddr)>>16),(u8)((WriteAddr)>>8),(u8)((WriteAddr)>>0)};
    W25QXX_Write_Enable();                  //SET WEL

    rt_completion_init(&spi_com);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_RESET);                            //使能器件
    HAL_SPI_Transmit_IT(SpiHandle,temp,4);
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    rt_completion_init(&spi_com);
    //HAL_SPI_Transmit_IT(SpiHandle,pBuffer,size);
		HAL_SPI_Transmit_DMA(SpiHandle,pBuffer,size);
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_SET);                            //取消片选
    W25QXX_Wait_Busy();					   //等待写入结束
}
//无检验写SPI FLASH
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(4096)
//CHECK OK
void W25QXX_Write_Sector(u8*pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{

    //u32 secpos=0;
    u16 secoff=0;
    u16 secremain=0;
    uint16_t this_write_count=0;
    //secpos=WriteAddr/4096;//扇区index 0~511 for w25x16
    while(NumByteToWrite>0)
    {
        secoff=WriteAddr%256;//在扇区内的偏移
        secremain=256-secoff;//扇区剩余空间大小
        this_write_count=NumByteToWrite>secremain?secremain:NumByteToWrite;
        W25QXX_Write_Page(pBuffer,WriteAddr,this_write_count);
        NumByteToWrite-=this_write_count;
        WriteAddr+=this_write_count;
        pBuffer+=this_write_count;
    }
}
void W25QXX_Write(u8*pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
    //u32 secpos=0;
    u16 secoff=0;
    u16 secremain=0;
    uint16_t this_write_count=0;
    //secpos=WriteAddr/4096;//扇区index 0~511 for w25x16
    while(NumByteToWrite>0)
    {
        secoff=WriteAddr%4096;//在扇区内的偏移
        secremain=4096-secoff;//扇区剩余空间大小
        this_write_count=NumByteToWrite>secremain?secremain:NumByteToWrite;
        //printf("write addr:%d,size:%d\n",WriteAddr,this_write_count);
        W25QXX_Write_Sector(pBuffer,WriteAddr,this_write_count);
        NumByteToWrite-=this_write_count;
        WriteAddr+=this_write_count;
        pBuffer+=this_write_count;
    }
}
//擦除整个芯片
//等待时间超长...
void W25QXX_Erase_Chip(void)
{
    u8 temp=W25X_ChipErase;
    W25QXX_Write_Enable();                  //SET WEL
    W25QXX_Wait_Busy();

    rt_completion_init(&spi_com);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_RESET);                            //使能器件
    HAL_SPI_Transmit_IT(SpiHandle,&temp,1);        //发送片擦除命令
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_SET);                            //取消片选
    W25QXX_Wait_Busy();   				   //等待芯片擦除结束
}
//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个扇区的最少时间:150ms
void W25QXX_Erase_Sector(u32 Dst_Addr)
{
    //监视falsh擦除情况,测试用
    //printf("fe:%x\r\n",Dst_Addr);
    u8 temp[4]={W25X_SectorErase,(u8)((Dst_Addr)>>16),(u8)((Dst_Addr)>>8),(u8)((Dst_Addr)>>0)};
    W25QXX_Write_Enable();                  //SET WEL
    W25QXX_Wait_Busy();

    rt_completion_init(&spi_com);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_RESET);                            //使能器件
    HAL_SPI_Transmit_IT(SpiHandle,temp,4);        //发送片擦除命令
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_SET);                            //取消片选
    W25QXX_Wait_Busy();   				    //等待擦除完成
}
//等待空闲
void W25QXX_Wait_Busy(void)
{
    while((W25QXX_ReadSR(1)&0x01)==0x01);   // 等待BUSY位清空
}
//进入掉电模式
void W25QXX_PowerDown(void)
{
    u8 temp=W25X_PowerDown;
    rt_completion_init(&spi_com);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_RESET); 			        //选中
    HAL_SPI_Transmit_IT(SpiHandle,&temp,1);
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_SET); 			        //选中
    rt_thread_delay(1);
}
//唤醒
void W25QXX_WAKEUP(void)
{
    u8 temp=W25X_ReleasePowerDown;
    rt_completion_init(&spi_com);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_RESET); 			        //选中
    HAL_SPI_Transmit_IT(SpiHandle,&temp,1);
    rt_completion_wait(&spi_com,RT_WAITING_FOREVER);
    HAL_GPIO_WritePin(F_CS_GPIO_Port,F_CS_Pin,GPIO_PIN_SET); 			        //选中
    rt_thread_delay(1);
}
